import { CPU } from "./cpu.js";
import { save_state, restore_state } from "./state.js";
export { V86 } from "./browser/starter.js";

/**
 * v86模拟器的核心类
 * @constructor
 * @param {Object} bus - 系统总线，用于设备间通信
 * @param {Object=} wasm - WebAssembly模块对象，可选
 */
export function v86(bus, wasm)
{
    /** @type {boolean} */
    this.running = false; // 模拟器是否正在运行

    /** @type {boolean} */
    this.stopping = false; // 模拟器是否正在停止

    /** @type {boolean} */
    this.idle = true; // 模拟器是否处于空闲状态

    this.tick_counter = 0; // 时钟计数器
    this.worker = null; // Web Worker，用于在后台处理异步任务

    /** @type {CPU} */
    this.cpu = new CPU(bus, wasm, () => { this.idle && this.next_tick(0); }); // 创建CPU实例

    this.bus = bus; // 保存总线引用

    this.register_yield(); // 注册yield机制，用于控制执行流程
}

/**
 * 启动模拟器
 */
v86.prototype.run = function()
{
    this.stopping = false; // 清除停止标志

    if(!this.running)
    {
        this.running = true; // 设置运行标志
        this.bus.send("emulator-started"); // 发送模拟器启动事件
    }

    this.next_tick(0); // 开始下一个执行周期
};

/**
 * 执行一个CPU周期
 */
v86.prototype.do_tick = function()
{
    if(this.stopping || !this.running)
    {
        this.stopping = this.running = false; // 重置运行状态
        this.bus.send("emulator-stopped"); // 发送模拟器停止事件
        return;
    }

    this.idle = false; // 标记为非空闲状态
    const t = this.cpu.main_loop(); // 执行CPU主循环，返回需要休眠的时间

    this.next_tick(t); // 安排下一个执行周期
};

/**
 * 安排下一个执行周期
 * @param {number} t - 下一个周期前需要等待的毫秒数
 */
v86.prototype.next_tick = function(t)
{
    const tick = ++this.tick_counter; // 增加时钟计数器
    this.idle = true; // 标记为空闲状态
    this.yield(t, tick); // 让出执行权，等待下一个周期
};

/**
 * yield操作的回调函数
 * @param {number} tick - 当前的时钟计数值
 */
v86.prototype.yield_callback = function(tick)
{
    if(tick === this.tick_counter) // 确保只有最新的yield操作才会继续执行
    {
        this.do_tick(); // 执行下一个CPU周期
    }
};

/**
 * 停止模拟器
 */
v86.prototype.stop = function()
{
    if(this.running)
    {
        this.stopping = true; // 设置停止标志
    }
};

/**
 * 销毁模拟器实例，释放资源
 */
v86.prototype.destroy = function()
{
    this.unregister_yield(); // 注销yield机制，释放相关资源
};

/**
 * 重启模拟器
 */
v86.prototype.restart = function()
{
    this.cpu.reset_cpu(); // 重置CPU
    this.cpu.load_bios(); // 重新加载BIOS
};

/**
 * 初始化模拟器
 * @param {Object} settings - 模拟器配置参数
 */
v86.prototype.init = function(settings)
{
    this.cpu.init(settings, this.bus); // 初始化CPU
    this.bus.send("emulator-ready"); // 发送模拟器就绪事件
};

if(typeof process !== "undefined")
{
    v86.prototype.yield = function(t, tick)
    {
        /* global global */
        if(t < 1)
        {
            global.setImmediate(tick => this.yield_callback(tick), tick);
        }
        else
        {
            setTimeout(tick => this.yield_callback(tick), t, tick);
        }
    };

    v86.prototype.register_yield = function() {};
    v86.prototype.unregister_yield = function() {};
}
else if(typeof Worker !== "undefined")
{
    // XXX: This has a slightly lower throughput compared to window.postMessage

    function the_worker()
    {
        let timeout;
        globalThis.onmessage = function(e)
        {
            const t = e.data.t;
            timeout = timeout && clearTimeout(timeout);
            if(t < 1) postMessage(e.data.tick);
            else timeout = setTimeout(() => postMessage(e.data.tick), t);
        };
    }

    v86.prototype.register_yield = function()
    {
        const url = URL.createObjectURL(new Blob(["(" + the_worker.toString() + ")()"], { type: "text/javascript" }));
        this.worker = new Worker(url);
        this.worker.onmessage = e => this.yield_callback(e.data);
        URL.revokeObjectURL(url);
    };

    v86.prototype.yield = function(t, tick)
    {
        this.worker.postMessage({ t, tick });
    };

    v86.prototype.unregister_yield = function()
    {
        this.worker && this.worker.terminate();
        this.worker = null;
    };
}
//else if(typeof window !== "undefined" && typeof postMessage !== "undefined")
//{
//    // setImmediate shim for the browser.
//    // TODO: Make this deactivatable, for other applications
//    //       using postMessage
//
//    const MAGIC_POST_MESSAGE = 0xAA55;
//
//    v86.prototype.yield = function(t)
//    {
//        // XXX: Use t
//        window.postMessage(MAGIC_POST_MESSAGE, "*");
//    };
//
//    let tick;
//
//    v86.prototype.register_yield = function()
//    {
//        tick = e =>
//        {
//            if(e.source === window && e.data === MAGIC_POST_MESSAGE)
//            {
//                this.do_tick();
//            }
//        };
//
//        window.addEventListener("message", tick, false);
//    };
//
//    v86.prototype.unregister_yield = function()
//    {
//        window.removeEventListener("message", tick);
//        tick = null;
//    };
//}
else
{
    v86.prototype.yield = function(t)
    {
        setTimeout(() => { this.do_tick(); }, t);
    };

    v86.prototype.register_yield = function() {};
    v86.prototype.unregister_yield = function() {};
}

v86.prototype.save_state = function()
{
    // TODO: Should be implemented here, not on cpu
    return save_state(this.cpu);
};

v86.prototype.restore_state = function(state)
{
    // TODO: Should be implemented here, not on cpu
    return restore_state(this.cpu, state);
};

/* global require */
if(typeof performance === "object" && performance.now)
{
    v86.microtick = performance.now.bind(performance);
}
else if(typeof require === "function")
{
    const { performance } = require("perf_hooks");
    v86.microtick = performance.now.bind(performance);
}
else if(typeof process === "object" && process.hrtime)
{
    v86.microtick = function()
    {
        var t = process.hrtime();
        return t[0] * 1000 + t[1] / 1e6;
    };
}
else
{
    v86.microtick = Date.now;
}
